import mqtt from 'mqtt';
import { createLogger, format, transports } from 'winston';
import { Device } from '../models/device';
import { DeviceData } from '../models/deviceData.model';
import { broadcast, broadcastDeviceStatus } from '../app';
import mongoose from 'mongoose';

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.timestamp(),
    format.json()
  ),
  transports: [
    new transports.Console(),
    new transports.File({ filename: 'mqtt.log' })
  ]
});

class MQTTService {
  private client!: mqtt.MqttClient;
  private static instance: MQTTService;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;
  private reconnectInterval = 10000;
  private deviceTimeout = 30000;
  private statusCheckInterval!: NodeJS.Timeout;

  private constructor() {
    this.connect();
    this.startStatusCheck();
  }

  public static getInstance(): MQTTService {
    if (!MQTTService.instance) {
      MQTTService.instance = new MQTTService();
    }
    return MQTTService.instance;
  }

  private connect() {
    const options = {
      host: process.env.MQTT_BROKER || 'mqtt-broker',
      port: parseInt(process.env.MQTT_PORT || '1883'),
      username: process.env.MQTT_USERNAME || 'iot_user',
      password: process.env.MQTT_PASSWORD,
      reconnectPeriod: parseInt(process.env.MQTT_RECONNECT_PERIOD || '10000'),
      connectTimeout: parseInt(process.env.MQTT_CONNECT_TIMEOUT || '30000'),
      keepalive: 60,
      clean: true,
      rejectUnauthorized: false
    };

    const url = `mqtt://${options.host}:${options.port}`;
    logger.info(`Attempting to connect to MQTT broker at ${url}`);
    
    this.client = mqtt.connect(url, {
      hostname: options.host,
      port: options.port,
      username: options.username,
      password: options.password,
      reconnectPeriod: options.reconnectPeriod,
      connectTimeout: options.connectTimeout,
      keepalive: options.keepalive,
      clean: options.clean,
      rejectUnauthorized: options.rejectUnauthorized
    });

    this.client.on('connect', () => {
      logger.info('Connected to MQTT broker');
      this.reconnectAttempts = 0;
      this.subscribeToTopics();
    });

    this.client.on('error', (error: Error) => {
      logger.error('MQTT error:', error);
    });

    this.client.on('close', () => {
      logger.warn('MQTT connection closed');
      this.handleReconnect();
    });

    this.client.on('message', this.handleMessage.bind(this));
  }

  private handleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      logger.info(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
      setTimeout(() => this.connect(), this.reconnectInterval);
    } else {
      logger.error('Max reconnection attempts reached');
      this.reconnectAttempts = 0;
      setTimeout(() => this.connect(), this.reconnectInterval * 2);
    }
  }

  private subscribeToTopics() {
    this.client.subscribe('device/+/data');
    this.client.subscribe('device/+/heartbeat');
    this.client.subscribe('device/+/status');
  }

  private async handleMessage(topic: string, message: Buffer) {
    try {
      const [_, deviceId, type] = topic.split('/');
      const data = JSON.parse(message.toString());

      switch (type) {
        case 'data':
          await this.handleDeviceData(topic, message);
          break;
        case 'heartbeat':
          await this.handleHeartbeat(deviceId);
          break;
        case 'status':
          await this.handleStatus(deviceId, data);
          break;
      }
    } catch (error) {
      logger.error('Error handling MQTT message:', error);
    }
  }

  private async handleDeviceData(topic: string, message: Buffer) {
    try {
      const deviceId = topic.split('/')[1];
      const data = JSON.parse(message.toString());
      
      const device = await Device.findOne({ deviceId });
      if (!device) {
        logger.error(`设备不存在: ${deviceId}`);
        return;
      }

      // 更新设备状态
      device.temperature = data.temperature;
      device.humidity = data.humidity;
      device.lastHeartbeat = new Date();
      device.status = 'online';
      await device.save();

      // 保存设备数据
      const deviceData = new DeviceData({
        deviceId,
        userId: device.userId,
        timestamp: new Date(),
        data: {
          temperature: data.temperature,
          humidity: data.humidity,
          battery: data.battery,
          signal: data.signal
        },
        metadata: {
          firmwareVersion: device.config?.firmwareVersion,
          ipAddress: device.config?.wifi?.ssid ? 'Connected' : undefined,
          connectionType: device.config?.wifi?.ssid ? 'wifi' : 'ethernet',
          signalStrength: device.config?.wifi?.signalStrength
        }
      });
      await deviceData.save();

      // 广播设备状态更新
      const stats = await this.getDeviceStats(device.userId.toString());
      broadcast('deviceUpdate', stats);
    } catch (error) {
      logger.error('处理设备数据时出错:', error);
    }
  }

  private async handleHeartbeat(deviceId: string) {
    try {
      const device = await Device.findOneAndUpdate(
        { deviceId },
        { 
          lastHeartbeat: new Date(),
          status: 'online'
        },
        { new: true }
      );
      
      if (!device) {
        logger.warn(`收到未知设备的心跳: ${deviceId}`);
        return;
      }

      // 广播设备状态更新
      const stats = await this.getDeviceStats(device.userId.toString());
      broadcast('deviceUpdate', stats);

      // 广播设备状态变化
      broadcastDeviceStatus(deviceId, 'online', {
        ipAddress: device.config?.wifi?.ssid ? 'Connected' : undefined,
        firmwareVersion: device.config?.firmwareVersion,
        connectionType: device.config?.wifi?.ssid ? 'wifi' : 'ethernet',
        signalStrength: device.config?.wifi?.signalStrength
      });
    } catch (error) {
      logger.error('处理设备心跳时出错:', error);
    }
  }

  private async handleStatus(deviceId: string, data: any) {
    try {
      const device = await Device.findOneAndUpdate(
        { deviceId },
        { status: data.status },
        { new: true }
      );

      if (!device) {
        logger.warn(`收到未知设备的状态更新: ${deviceId}`);
        return;
      }

      // 广播设备状态更新
      const stats = await this.getDeviceStats(device.userId.toString());
      broadcast('deviceUpdate', stats);

      // 广播设备状态变化
      broadcastDeviceStatus(deviceId, data.status, {
        ipAddress: data.ipAddress,
        firmwareVersion: data.firmwareVersion,
        connectionType: data.connectionType,
        signalStrength: data.signalStrength
      });
    } catch (error) {
      logger.error('处理设备状态更新时出错:', error);
    }
  }

  public async getDeviceStats(userId: string) {
    try {
      const devices = await Device.find({ userId: new mongoose.Types.ObjectId(userId) });
      return {
        totalDevices: devices.length,
        onlineDevices: devices.filter(d => d.status === 'online').length,
        offlineDevices: devices.filter(d => d.status === 'offline').length,
        devices: devices.map(d => ({
          deviceId: d.deviceId,
          name: d.name,
          type: d.type,
          status: d.status,
          temperature: d.temperature,
          humidity: d.humidity,
          lastHeartbeat: d.lastHeartbeat
        }))
      };
    } catch (error) {
      logger.error('Error getting device stats:', error);
      return {
        totalDevices: 0,
        onlineDevices: 0,
        offlineDevices: 0,
        devices: []
      };
    }
  }

  private startStatusCheck() {
    this.statusCheckInterval = setInterval(async () => {
      try {
        const now = new Date();
        const timeoutThreshold = new Date(now.getTime() - this.deviceTimeout);

        // 查找所有最后心跳时间超过阈值的在线设备
        const devices = await Device.find({
          status: 'online',
          lastHeartbeat: { $lt: timeoutThreshold }
        });

        // 将这些设备标记为离线
        for (const device of devices) {
          await Device.findOneAndUpdate(
            { deviceId: device.deviceId },
            { status: 'offline' }
          );
          logger.info(`Device ${device.deviceId} marked as offline due to timeout`);

          // 广播设备离线状态
          broadcastDeviceStatus(device.deviceId, 'offline', {
            lastSeen: device.lastHeartbeat
          });
        }
      } catch (error) {
        logger.error('Error checking device status:', error);
      }
    }, 10000); // 每10秒检查一次
  }

  public publish(topic: string, message: any) {
    this.client.publish(topic, JSON.stringify(message));
  }
}

export default MQTTService.getInstance(); 